package org.example.log4j2;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * 面向 log4j-api 原生接口编程，动态设置 log4j2 日志输出级别。
 * 1、即使没有提供 log4j.xml 配置文件，只要引入了依赖，则默认日志输出级别为 info，照样能切换日志输出级别.
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/6/9 16:50
 */
@RestController
@SuppressWarnings("all")
public class Log4j2Controller {
    private static final Logger LOGGER = LogManager.getLogger();

    /**
     * 打印日志测试
     * http://localhost:8080/log4j/logs
     */
    @GetMapping("log4j/logs")
    public void logs() {
        LOGGER.trace("hello, I am trace!");
        LOGGER.debug("hello, I am debug!");
        LOGGER.info("hello, I am info!");
        LOGGER.warn("hello, I am {}!", "warn");
        LOGGER.error("hello, I am {}!", "error");
    }

    /**
     * 获取 root(根)日志输出级别
     * http://localhost:8080/log4j/getRootLevel
     *
     * @return
     */
    @GetMapping(value = "/log4j/getRootLevel")
    public Map<String, Object> getRootLevel() {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("code", 200);
        dataMap.put("msg", "查询成功");
        try {
            /**返回根记录器, 对应 {@link LogManager#ROOT_LOGGER_NAME}.*/
            Logger rootLogger = LogManager.getRootLogger();
            //Level getLevel()：获取与记录器关联的级别.
            String rootLoggerLevel = rootLogger.getLevel().name();
            dataMap.put("data", rootLoggerLevel);
        } catch (Exception e) {
            dataMap.put("code", 500);
            dataMap.put("msg", e.getMessage());
            e.printStackTrace();
        }
        return dataMap;
    }

    /**
     * Log4j2 动态修改 root(根)日志级别，修改之后配置立刻生效.
     * http://localhost:8080/log4j/setRootLevel?level=TRACE
     *
     * @param level ：可选值有：ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF，否则默认会设置为 DEBUG，不区分大小写。
     * @return
     */
    @GetMapping(value = "/log4j/setRootLevel")
    public Map<String, Object> setRootLevel(@RequestParam(value = "level") String level) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("code", 200);
        dataMap.put("msg", "修改root日志级别成功");

        try {
            //LoggerContext getContext(final boolean currentContext)：获取 log4j 日志上下文，false 表示返回合适调用方的上下文
            LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);

            //返回当前配置，发生重新配置时，将替换该配置。
            Configuration configuration = loggerContext.getConfiguration();
            //查找记录器名称的相应 LoggerConfig
            LoggerConfig loggerConfig = configuration.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);

            //设置日志级别
            //如果 level 值不属于 ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF，则默认会设置为 DEBUG.
            Level toLevel = Level.toLevel(level);
            loggerConfig.setLevel(toLevel);

            //根据当前配置更新所有记录器
            loggerContext.updateLoggers();

            //查询 root(根)日志输出级别结果返回
            String root_logger_name = LogManager.getRootLogger().getLevel().name();
            dataMap.put("data", root_logger_name);
        } catch (Exception e) {
            dataMap.put("code", 500);
            dataMap.put("msg", e.getMessage());
            e.printStackTrace();
        }
        return dataMap;
    }

    /**
     * 查询指定包或者类的日志输出级别，路径不存在时，默认返回 root(根)记录器的级别
     * http://localhost:8080/log4j/getLoggerLevel?className=org.mybatis
     *
     * @param className ：包名或者类名，比如：org.mybatis、org.mybatis.xxx
     *                  log4j2.xml 中 logger 优先级高于 root，没有指定 logger 的包或者类默认为与 root(根)级别一致.
     *                  这个参数路径不存在也没关系，此时返回root(根)级别。
     * @return
     */
    @GetMapping(value = "/log4j/getLoggerLevel")
    public Map<String, Object> getLoggerLevel(@RequestParam String className) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("code", 200);
        dataMap.put("msg", "查询成功");
        dataMap.put("className", className);
        try {
            //返回具有指定名称的记录器,不存在时返回root(根)记录器
            Logger logger = LogManager.getLogger(className);
            String name = logger.getLevel().name();

            dataMap.put("data", name);
        } catch (Exception e) {
            dataMap.put("code", 500);
            dataMap.put("msg", e.getMessage());
            e.printStackTrace();
        }
        return dataMap;
    }

    /**
     * 设置指定包或者类的日志输出级别，指定路径不存在时，默认设置整个 root(根)记录器，修改之后配置立刻生效.
     * http://localhost:8080/log4j/setLoggerLevel?className=org.mybatis&level=ALL
     *
     * @param className ：包名或者类名，比如：org.mybatis、org.mybatis.xxx
     *                  log4j2.xml 中 logger 优先级高于 root，没有指定 logger 的包或者类默认为与 root(根)级别一致.
     *                  如果路径不存在，则默认设置是整个 root(根)记录器
     * @param level     ：日志级别，可选值有：ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF，否则默认会设置为 DEBUG，不区分大小写。
     * @return
     */
    @GetMapping(value = "/log4j/setLoggerLevel")
    public Map<String, Object> setLoggerLevel(@RequestParam String className,
                                              @RequestParam(value = "level") String level) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("code", 200);
        dataMap.put("msg", "修改指定路径日志级别成功");
        dataMap.put("className", className);
        try {
            //LoggerContext getContext(final boolean currentContext)：获取 log4j 日志上下文，false 表示返回合适调用方的上下文
            LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);

            //返回当前配置，发生重新配置时，将替换该配置。
            Configuration configuration = loggerContext.getConfiguration();

            //返回具有指定名称的记录器
            Logger logger = LogManager.getLogger(className);

            //查找记录器名称的相应 LoggerConfig, 不存在时返回root(根)记录器的配置
            LoggerConfig loggerConfig = configuration.getLoggerConfig(logger.getName());

            //设置日志级别
            //如果 level 值不属于 ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF，则默认会设置为 DEBUG.
            Level toLevel = Level.toLevel(level);
            loggerConfig.setLevel(toLevel);

            //根据当前配置更新所有记录器
            loggerContext.updateLoggers();

            //查询 root(根)日志输出级别结果返回
            String root_logger_name = LogManager.getRootLogger().getLevel().name();
            dataMap.put("data", root_logger_name);
        } catch (Exception e) {
            dataMap.put("code", 500);
            dataMap.put("msg", e.getMessage());
            e.printStackTrace();
        }
        return dataMap;
    }

}
